home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
JFC.bin
/
HTMLEditorKit.java
< prev
next >
Wrap
Text File
|
1998-06-30
|
32KB
|
1,209 lines
/*
* @(#)HTMLEditorKit.java 1.30 98/04/12
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.text.html;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import com.sun.java.swing.Action;
import com.sun.java.swing.text.*;
import com.sun.java.swing.*;
import com.sun.java.swing.event.*;
import java.util.*;
/**
* This is the default implementation of html editing
* functionality. The primary goal with this is to
* be small, but flexible. It is not intended to be
* an all singing and all dancing html implementation.
* This is provided to meet more modest needs, with
* the idea that more substantial needs can be met
* with alternative implementations.
*
* @author Timothy Prinzing
* @author Makarand Gokhale
* @version 1.30 04/12/98
*/
public class HTMLEditorKit extends StyledEditorKit {
/**
* Constructs an HTMLEditorKit, creates a StyleContext,
* and loads the style sheet.
*/
public HTMLEditorKit() {
styleContext = new StyleContext();
loadStyleSheet(styleContext);
}
/**
* Create a copy of the editor kit. This
* allows an implementation to serve as a prototype
* for others, so that they can be quickly created.
*
* @return the copy
*/
public Object clone() {
return new HTMLEditorKit();
}
/**
* Get the MIME type of the data that this
* kit represents support for. This kit supports
* the type <code>text/html</code>.
*
* @return the type
*/
public String getContentType() {
return "text/html";
}
/**
* Fetch a factory that is suitable for producing
* views of any models that are produced by this
* kit.
*
* @return the factory
*/
public ViewFactory getViewFactory() {
return new HTMLFactory();
}
/**
* Create an uninitialized text storage model
* that is appropriate for this type of editor.
*
* @return the model
*/
public Document createDefaultDocument() {
StyledDocument doc = new HTMLDocument(styleContext);
return doc;
}
/**
* Create and initialize a model from the given
* stream which is expected to be in a format appropriate
* for this kind of editor. This is implemented to read
* html 3.2 text.
*
* @param in The stream to read from
* @param doc The destination for the insertion.
* @param pos The location in the document to place the
* content.
* @exception IOException on any I/O error
* @exception BadLocationException if pos represents an invalid
* location within the document.
*/
public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException {
if (doc instanceof HTMLDocument) {
HTMLDocument hdoc = (HTMLDocument) doc;
Parser p = getParser();
ParserCallback receiver = hdoc.getReader(pos);
p.parse(in, receiver);
receiver.flush();
} else {
super.read(in, doc, pos);
}
}
/**
* Write content from a document to the given stream
* in a format appropriate for this kind of content handler.
*
* @param out The stream to write to
* @param doc The source for the write.
* @param pos The location in the document to fetch the
* content.
* @param len The amount to write out.
* @exception IOException on any I/O error
* @exception BadLocationException if pos represents an invalid
* location within the document.
*/
public void write(Writer out, Document doc, int pos, int len)
throws IOException, BadLocationException {
if (doc instanceof StyledDocument) {
try {
HTMLWriter w = new HTMLWriter();
w.write(out, (StyledDocument)doc);
} catch (Throwable e) {
throw new IOException(e.getMessage());
}
} else {
super.write(out, doc, pos, len);
}
}
/**
* Called when the kit is being installed into the
* a JEditorPane.
*
* @param c the JEditorPane
*/
public void install(JEditorPane c) {
c.addMouseListener(linkHandler);
super.install(c);
}
/**
* Called when the kit is being removed from the
* JEditorPane. This is used to unregister any
* listeners that were attached.
*
* @param c the JEditorPane
*/
public void deinstall(JEditorPane c) {
c.removeMouseListener(linkHandler);
super.deinstall(c);
}
/**
* Default Cascading Style Sheet file that sets
* up the tag views.
*/
public static final String DEFAULT_CSS = "default.css";
/**
* Load in the default.css file.
*/
private void loadStyleSheet(StyleContext sc) {
InputStream is = this.getClass().getResourceAsStream(DEFAULT_CSS);
if (is == null)
System.out.println("HTMLEditorKit.loadStyleSheet: " + DEFAULT_CSS + " file not found");
else {
//
// Create the StyleReader and call it.
//
StyleReader stylereader = new StyleReader(sc);
stylereader.read(sc, 0, is);
}
}
/**
* Fetches the command list for the editor. This is
* the list of commands supported by the superclass
* augmented by the collection of commands defined
* locally for style operations.
*
* @return the command list
*/
public Action[] getActions() {
return TextAction.augmentList(super.getActions(), this.defaultActions);
}
/**
* Fetch the parser to use for reading html streams.
* This can be reimplemented to provide a different
* parser. The default implementation is loaded dynamically
* to avoid the overhead of loading the default parser if
* it's not used. The default parser is based upon
* <a href="http://suntest.sun.com/JavaCC/index.html">JavaCC</a>,
* with the grammar defined in the file <code>html-3.2.jj</code>.
* One can replace the parser using a customized grammar, or
* replace the parser with one that doesn't use
* the JavaCC parser generator.
*/
protected Parser getParser() {
if (defaultParser == null) {
try {
Class c = Class.forName("com.sun.java.swing.text.html.html32$DefaultParser");
defaultParser = (Parser) c.newInstance();
} catch (Throwable e) {
}
}
return defaultParser;
}
// --- variables ------------------------------------------
private StyleContext styleContext = null;
private MouseListener linkHandler = new LinkController();
private static Parser defaultParser = null;
/**
* Class to watch the associated component and fire
* hyperlink events on it when appropriate.
*/
public static class LinkController extends MouseAdapter {
/**
* Called for a mouse click event.
* If the component is read-only (ie a browser) then
* the clicked event is used to drive an attempt to
* follow the reference specified by a link.
*
* @param e the mouse event
* @see MouseListener#mouseClicked
*/
public void mouseClicked(MouseEvent e) {
JEditorPane editor = (JEditorPane) e.getSource();
if (! editor.isEditable()) {
Point pt = new Point(e.getX(), e.getY());
int pos = editor.viewToModel(pt);
if (pos >= 0) {
activateLink(pos, editor);
}
}
}
/**
* Calls linkActivated on the associated JEditorPane
* if the given position represents a link.
*
* @param pos the position
* @param html the editor pane
*/
protected final void activateLink(int pos, JEditorPane html) {
Document doc = html.getDocument();
if (doc instanceof StyledDocument) {
StyledDocument sdoc = (StyledDocument) doc;
Element e = sdoc.getCharacterElement(pos);
AttributeSet a = e.getAttributes();
String href = (String) a.getAttribute("href");
if (href != null) {
URL u;
try {
u = new URL(html.getPage(), href);
} catch (MalformedURLException m) {
u = null;
}
HyperlinkEvent linkEvent =
new HyperlinkEvent(html, HyperlinkEvent.EventType.ACTIVATED, u);
html.fireHyperlinkUpdate(linkEvent);
}
}
}
}
/**
* Interface to be supported by the parser. This enables
* providing a different parser while reusing some of the
* implementation provided by this editor kit.
*/
/*public*/ interface Parser {
/**
* Parse the given stream and drive the given callback
* with the results of the parse. This method should
* be implemented to be thread-safe.
*/
public void parse(Reader r, ParserCallback cb) throws IOException;
}
/**
* The result of parsing drives these callback methods.
* The open and close actions should be balanced. The
* <code>flush</code> method will be the last method
* called, to give the receiver a chance to flush any
* pending data into the document.
*/
/*public*/ static class ParserCallback {
/* PENDING(prinz) The following is what the parser
* communication is expected to look like shortly.
* We intend to replace the default parser as well.
*
* public void flush() throws BadLocationException {
* }
*
* public void pcdata(String data) {
* }
*
* public void comment(String data) {
* }
*
* public void startTag(Constants.Tag t, AttributeSet a) {
* }
*
* public void endTag(Constants.Tag t) {
* }
*
* public void simpleTag(Constants.Tag t, AttributeSet a) {
* }
*/
public void flush() throws BadLocationException {
}
public void pcdataAction(String data) {
}
public void whitespaceAction(String data) {
}
public void attributeAction(String name, String value) {
}
public void blockOpenAction(String tag) {
}
public void blockCloseAction(String tag) {
}
public void aOpenAction() {
}
public void aCloseAction() {
}
public void imgAction() {
}
public void hrAction() {
}
public void fontOpenAction() {
}
public void fontCloseAction() {
}
public void iOpenAction() {
}
public void iCloseAction() {
}
public void bOpenAction() {
}
public void bCloseAction() {
}
public void uOpenAction() {
}
public void uCloseAction() {
}
public void strikeOpenAction() {
}
public void strikeCloseAction() {
}
public void liOpenAction() {
}
public void liCloseAction() {
}
public void ulOpenAction() {
}
public void ulCloseAction() {
}
public void olOpenAction() {
}
public void olCloseAction() {
}
public void dlOpenAction() {
}
public void dlCloseAction() {
}
public void ddOpenAction() {
}
public void ddCloseAction() {
}
public void dtOpenAction() {
}
public void dtCloseAction() {
}
public void dirOpenAction() {
}
public void dirCloseAction() {
}
public void menuOpenAction() {
}
public void menuCloseAction() {
}
public void htmlOpenAction() {
}
public void htmlCloseAction() {
}
public void headOpenAction() {
}
public void headCloseAction() {
}
public void bodyOpenAction() {
}
public void bodyCloseAction() {
}
public void titleOpenAction() {
}
public void titleCloseAction() {
}
public void preOpenAction() {
}
public void preCloseAction() {
}
public void ttOpenAction(){
}
public void ttCloseAction(){
}
public void bigOpenAction(){
}
public void bigCloseAction(){
}
public void smallOpenAction(){
}
public void smallCloseAction(){
}
public void blockquoteOpenAction(){
}
public void blockquoteCloseAction(){
}
public void emOpenAction(){
}
public void emCloseAction(){
}
public void strongOpenAction(){
}
public void strongCloseAction(){
}
public void varOpenAction(){
}
public void varCloseAction(){
}
public void basefontAction(){
}
public void brAction(){
}
public void centerOpenAction(){
}
public void centerCloseAction(){
}
public void citeOpenAction(){
}
public void citeCloseAction(){
}
public void kbdOpenAction(){
}
public void kbdCloseAction(){
}
public void subOpenAction(){
}
public void subCloseAction(){
}
public void supOpenAction(){
}
public void supCloseAction(){
}
public void dfnOpenAction(){
}
public void dfnCloseAction(){
}
public void codeOpenAction(){
}
public void codeCloseAction(){
}
public void sampOpenAction(){
}
public void sampCloseAction(){
}
public void addressOpenAction(){
}
public void addressCloseAction(){
}
public void divOpenAction(){
}
public void divCloseAction(){
}
public void mapOpenAction(){
}
public void mapCloseAction(){
}
public void areaAction(){
}
public void linkAction(){
}
public void paramAction(){
}
public void inputAction(){
}
public void appletOpenAction(){
}
public void appletCloseAction(){
}
public void formOpenAction(){
}
public void formCloseAction(){
}
public void selectOpenAction(){
}
public void selectCloseAction(){
}
public void optionOpenAction(){
}
public void optionCloseAction(){
}
public void textareaOpenAction(){
}
public void textareaCloseAction(){
}
public void tableOpenAction(){
}
public void tableCloseAction(){
}
public void trOpenAction(){
}
public void trCloseAction(){
}
public void thOpenAction(){
}
public void thCloseAction(){
}
public void tdOpenAction(){
}
public void tdCloseAction(){
}
public void captionOpenAction(){
}
public void captionCloseAction(){
}
public void isindexAction(){
}
public void baseAction(){
}
public void metaAction(){
}
public void styleOpenAction(){
}
public void styleCloseAction(){
}
public void scriptOpenAction(){
}
public void scriptCloseAction(){
}
}
/**
* A factory to build view fragments for html.
*/
public static class HTMLFactory implements ViewFactory {
/**
* Creates a view from an element.
*
* @param elem the element
* @return the view
*/
public View create(Element elem) {
String kind = elem.getName();
if (kind != null) {
String ikind = kind.intern();
if (ikind == AbstractDocument.ContentElementName) {
// text content
return new LabelView(elem);
} else if ((ikind == AbstractDocument.ParagraphElementName) || (ikind == Constants.DT)) {
// paragraph
return new ParagraphView(elem);
} else if ((ikind == Constants.MENU) || (ikind == Constants.DIR) ||
(ikind == Constants.UL) || (ikind == Constants.OL)) {
return new ListView(elem);
} else if (ikind == AbstractDocument.SectionElementName) {
return new BodyView(elem, View.Y_AXIS);
} else if (ikind == Constants.PRELINE) {
return new LineView(elem);
} else if ((ikind == Constants.LI) || (ikind == Constants.DL) ||
(ikind == Constants.DD) || (ikind == Constants.PRE)) {
// vertical box
return new HTMLBoxView(elem, View.Y_AXIS);
} else if ((ikind == Constants.FORM)) {
// horizontal box
return new HTMLBoxView(elem, View.X_AXIS);
} else if (ikind==StyleConstants.ComponentElementName) {
return new ComponentView(elem);
} else if (ikind==Constants.IMG) {
return new ImageView(elem);
} else if (ikind == Constants.HR) {
return new HRuleView(elem);
} else if (ikind == Constants.TABLE ) {
return new TableView(elem);
}
}
// don't know how to build this....
return null;
}
}
// --- Action implementations ------------------------------
/** The bold action identifier
*/
public static final String BOLD_ACTION = "html-bold-action";
/** The italic action identifier
*/
public static final String ITALIC_ACTION = "html-italic-action";
/** The paragraph left indent action identifier
*/
public static final String PARA_INDENT_LEFT = "html-para-indent-left";
/** The paragraph right indent action identifier
*/
public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
/** The font size increase to next value action identifier
*/
public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
/** The font size decrease to next value action identifier
*/
public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
/** The Color choice action identifier
The color is passed as an argument
*/
public static final String COLOR_ACTION = "html-color-action";
/** The logical style choice action identifier
The logical style is passed in as an argument
*/
public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
/**
* Align images at the top.
*/
public static final String IMG_ALIGN_TOP = "html-image-align-top";
/**
* Align images in the middle.
*/
public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
/**
* Align images at the bottom.
*/
public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
/**
* Align images at the border.
*/
public static final String IMG_BORDER = "html-image-border";
// --- Action implementations ---------------------------------
private static final Action[] defaultActions = {
new HTMLBoldAction(),
new HTMLItalicAction(),
new FontSizeChangeAction(FONT_CHANGE_BIGGER, true),
new FontSizeChangeAction(FONT_CHANGE_SMALLER, false),
new ParagraphIndentAction(PARA_INDENT_LEFT, true),
new ParagraphIndentAction(PARA_INDENT_RIGHT, false),
new ImgAlignAction(IMG_ALIGN_TOP, ImageView.TOP),
new ImgAlignAction(IMG_ALIGN_MIDDLE, ImageView.MIDDLE),
new ImgAlignAction(IMG_ALIGN_BOTTOM, ImageView.BOTTOM),
new ImgBorderAction(IMG_BORDER),
new ForegroundAction(COLOR_ACTION, Color.black),
new LogicalStyleAction("Normal")
};
static abstract class HtmlAction extends StyledTextAction {
HtmlAction(String nm) {
super(nm);
}
/**
* Applies the given attributes to character
* content. The attributes are applied to a range. The range
* must be within current selection
* If the the start and end point are the same then
* the attributes are applied to
* the input attribute set which defines the attributes
* for any new text that gets inserted.
*
* @param attr the attributes
* @param start The starting position for a range
* @param end the ending position for a range
* @param replace if true, then replace the existing attributes first
*/
protected final void setCharacterAttributes(JEditorPane editor,
AttributeSet attr, int start,
int end, boolean replace) {
if(start > end) {
int tmp = end;
end = start;
start = tmp;
}
int p0 = editor.getSelectionStart();
int p1 = editor.getSelectionEnd();
if(start < p0) start = p0;
if(end > p1 ) end = p1;
if (start != end) {
StyledDocument doc = getStyledDocument(editor);
doc.setCharacterAttributes(start, end - start, attr, replace);
} else {
StyledEditorKit k = getStyledEditorKit(editor);
MutableAttributeSet inputAttributes = k.getInputAttributes();
if (replace) {
inputAttributes.removeAttributes(inputAttributes);
}
inputAttributes.addAttributes(attr);
}
}
}
/**
* An action to toggle the bold attribute
*/
static class HTMLBoldAction extends HtmlAction {
public HTMLBoldAction() {
super(BOLD_ACTION);
}
public void actionPerformed(ActionEvent ae) {
JTextComponent target = getFocusedComponent();
if (target != null) {
JEditorPane pane = (JEditorPane) target;
MutableAttributeSet attr = new SimpleAttributeSet();
// Check if the event source is a toggle button
// If so set or unset bold based on toggle state
Object o = ae.getSource();
boolean bSet=true;
if (o != null && o instanceof JToggleButton)
bSet = ((JToggleButton)o).isSelected();
StyleConstants.setBold(attr, bSet);
setCharacterAttributes(pane, attr, false);
}
}
}
/**
* An action to toggle the italic attribute
*/
static class HTMLItalicAction extends HtmlAction {
public HTMLItalicAction() {
super(ITALIC_ACTION);
}
public void actionPerformed(ActionEvent ae) {
JTextComponent target = getFocusedComponent();
if (target != null) {
JEditorPane pane = (JEditorPane) target;
MutableAttributeSet attr = new SimpleAttributeSet();
// Check if the event source is a toggle button
// If so set or unset italic based on toggle state
Object o = ae.getSource();
boolean bSet=true;
if (o != null && o instanceof JToggleButton)
bSet = ((JToggleButton)o).isSelected();
StyleConstants.setItalic(attr, bSet);
setCharacterAttributes(pane,attr, false);
}
}
}
/**
*
* This class supports the font size change action. When the button
* is clicked on a toolbar it sends an action event to this class
* This class defines a local variable to specify, whether to increas
* or decrease the font size. It obtains the next size using HTMLStyleSheet
*
* The font size is changed only if there is only one style in effect
* for a selection. Otherwise the change request is ignored.
*
* The following is the order of processing
* 1. Check if there is a range of selection.
* 2. If not set the fontsize for the input
& 3. If ma range is selected then get all the effective styles
* 4. If more than one size exists on a Font Ignore change request
* 5. Otherwise change font
*/
static class FontSizeChangeAction extends HtmlAction {
private boolean bIncrease = true;
FontSizeChangeAction( String key, boolean b) {
super(key);
bIncrease = b;
}
public void actionPerformed(ActionEvent event) {
JTextComponent target = getFocusedComponent();
StyleSheet ss = StyleReader.getStyleSheet();
if (target != null) {
JEditorPane pane = (JEditorPane) target;
Caret ip = pane.getCaret();
int pos = ip.getDot();
if (ip.getMark() == pos) {
/* ASK TIM HOW TO GET INPUTATTRIBUTES
AttributeSet as = pane.getInputAttributes();
*/
AttributeSet as = null;
if (as != null) {
int size = StyleConstants.getFontSize(as);
size= (bIncrease?ss.getBigger(size):ss.getSmaller(size));
MutableAttributeSet attr = new SimpleAttributeSet();
StyleConstants.setFontSize(attr, size);
setCharacterAttributes(pane,attr, false);
}
}
else {
int start = ip.getMark();
int end = ip.getDot();
Vector v = getCharacterElements(pane, start,end);
if (v != null ) {
int size=-1;
int nextSize=-1;
int vSize=v.size();
if (vSize > 0 ) {
boolean bAbort = false;
int SizeArray[] = new int[vSize];
for (int i=0; i < vSize && !bAbort; i++) {
try {
AttributeSet as = ((Element)v.elementAt(i)).getAttributes();
size= StyleConstants.getFontSize(as);
nextSize = (bIncrease?ss.getBigger(size):ss.getSmaller(size));
if (size == nextSize) {
System.out.println("Bigger/Smaller ABORTING:Size limit reached for element:"+((Element)v.elementAt(i)).getName());
bAbort = true;
continue;
}
SizeArray[i] = nextSize;
} catch(NullPointerException ee1) {
System.out.println("Exception: Cannot find font size for element:"+i);
SizeArray[i] = -1;
bAbort = true;
continue;
}
}
if (!bAbort) {
int eStart, eEnd;
for (int i=0; i <vSize;i++) {
Element elem = (Element)v.elementAt(i);
eStart = elem.getStartOffset();
eEnd = elem.getEndOffset();
if (eStart < start) eStart = start;
if (eEnd > end) eEnd = end;
if (SizeArray[i] > 0) {
MutableAttributeSet attr = new SimpleAttributeSet();
StyleConstants.setFontSize(attr, SizeArray[i]);
setCharacterAttributes(pane,attr,eStart, eEnd, false);
}
}
}
else{
System.out.println("Atleast one size at the limit, Aborting bigger/smaller");
}
}
}
}
}
}
}
/**
* ParagraphIndentAction sets a new alignment. If it is instantiated
* with bInc=true then the next alignment will be chosen in order
* from left to right. If bInc=false then the next alignment will be
* in the order from right to left.
*/
static class ParagraphIndentAction extends HtmlAction {
boolean bLeft = false;
ParagraphIndentAction(String key, boolean b) {
super(key);
bLeft = b;
}
public void actionPerformed(ActionEvent e) {
JEditorPane pane = (JEditorPane)getFocusedComponent();
if (pane != null) {
StyledEditorKit k = getStyledEditorKit(pane);
MutableAttributeSet attr = k.getInputAttributes();
if (attr != null) {
int align = StyleConstants.getAlignment(attr);
if (!bLeft) {
if (align == StyleConstants.ALIGN_LEFT) {
align = StyleConstants.ALIGN_CENTER;
}
else if (align == StyleConstants.ALIGN_CENTER) {
align = StyleConstants.ALIGN_RIGHT;
}
}
else {
if (align == StyleConstants.ALIGN_CENTER) {
align = StyleConstants.ALIGN_LEFT;
}
else if (align == StyleConstants.ALIGN_RIGHT) {
align = StyleConstants.ALIGN_CENTER;
}
}
StyleConstants.setAlignment(attr, align);
setParagraphAttributes(pane,attr, false);
}
}
}
}
/**
* Set the Logical Style on the paragraph.
* It calls the setLogicalStyle method on the document
*/
static class LogicalStyleAction extends HtmlAction {
String styleStr;
LogicalStyleAction(String styleStr) {
super(LOGICAL_STYLE_ACTION);
this.styleStr = styleStr;
}
public void actionPerformed(ActionEvent e) {
JEditorPane editor = getEditor(e);
if (editor != null) {
String styleName = styleStr;
if ((e != null) && (e.getSource() == editor)) {
String s = e.getActionCommand();
if (s != null)
styleName = s;
}
StyledDocument doc = (StyledDocument) editor.getDocument();
Style style = doc.getStyle(styleStr);
if (style != null) {
doc.setLogicalStyle(editor.getCaretPosition(),style);
}
}
}
}
/**
* A method to get all the elements in the range
*/
static Vector getCharacterElements(JEditorPane editorpane,int start, int end) {
StyledDocument doc = (StyledDocument)editorpane.getDocument();
Vector v = new Vector();
int pos = start;
start= (start> end?end:start);
end = (start > end?pos:end);
pos = start;
while (pos <= end) {
Element e = doc.getCharacterElement(pos);
if ( e== null) {
pos++;
continue;
}
v.addElement(e);
if (pos < e.getEndOffset()+1)
pos = e.getEndOffset()+1;
else
pos++;
}
return v;
}
/**
* An action to align images top, middle, bottom
*/
static class ImgAlignAction extends HtmlAction {
String align;
/** Create an action to set an image (vertical) alignment.
@param name The name of this action
@param align The value of the ALIGN attribute. Correct values
are "top", "middle", "bottom" (see consts in ImageView) */
public ImgAlignAction(String name, String align) {
super(name);
this.align = align;
}
public void actionPerformed(ActionEvent ae) {
if(DEBUG)System.out.println("ImgAlignAction: set align to "+align);
JTextComponent target = getFocusedComponent();
if (target != null) {
JEditorPane pane = (JEditorPane) target;
MutableAttributeSet attr = new SimpleAttributeSet();
attr.addAttribute(Constants.ALIGN,align);
setCharacterAttributes(pane, attr, false);
//((HTMLDocument)pane.getDocument()).dump(System.out);//$$$$$ TEST
}
}
}
/**
* Toggle the border on an img.
*/
static class ImgBorderAction extends HtmlAction {
public ImgBorderAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent ae) {
JEditorPane pane = (JEditorPane) getFocusedComponent();
AttributeSet attr = getImageAttributes(pane);
if( attr != null ) {
// Figure out if it currently has a border:
boolean link = attr.isDefined(Constants.HREF);
int border = link ?2 :0; // border is default in link
String borderStr = (String) attr.getAttribute(Constants.BORDER);
if( borderStr != null ) {
try{
border = Integer.parseInt(borderStr);
}catch( NumberFormatException x ) {
}
}
// Work out the new 'border' attribute value:
MutableAttributeSet newAttr = new SimpleAttributeSet();
boolean replace = false;
if( border == 0 && !link ) {
if(DEBUG)System.out.println("ImgBorderAction: set border=2");
newAttr.addAttribute(Constants.BORDER,"2");
} else if( border != 0 && link ) {
if(DEBUG)System.out.println("ImgBorderAction: set border=0");
newAttr.addAttribute(Constants.BORDER,"0");
} else {
// need to remove 'border' attribute entirely:
if(DEBUG)System.out.println("ImgBorderAction: reset border");
newAttr.addAttributes(attr);
newAttr.removeAttribute(Constants.BORDER);
replace = true;
}
// Update/replace attributes:
setCharacterAttributes(pane, newAttr, replace);
}
}
/** If an image is selected, return its AttributeSet. */
private AttributeSet getImageAttributes( JEditorPane editorpane ) {
if (editorpane != null) {
int start = editorpane.getSelectionStart();
int end = editorpane.getSelectionEnd();
if( Math.abs(end-start)==1 ) {
StyledDocument htmldoc = (StyledDocument) editorpane.getDocument();
Element e = htmldoc.getCharacterElement(Math.min(start,end));
if( e != null && e.getName().equals(Constants.IMG) )
return e.getAttributes();
}
}
return null;
}
}
static final boolean DEBUG = false;
}